using System;
using System.Collections.Generic;
using System.Xml;
using System.Xml.Serialization;
using System.IO;
using System.Net.Mail;
using System.Runtime.CompilerServices;

namespace WeatherRouter.Model
{
	/// <summary>
	/// Summary description for config.
	/// </summary>
	public class Configuration
	{
        private const string CONFIG_FILENAME = "configuration.xml";

        private string _configPath;
        private List<Subscriber> _subscribers;
        private List<Feed> _feeds;        
        private Proxy _proxy;
        private Smtp _smtp;        
        private FileSystemWatcher _watcher;

        private static Configuration _instance;

        /// <summary>
        /// Default constructor.
        /// </summary>
        public Configuration()
        {
            _subscribers = new List<Subscriber>();
            _feeds = new List<Feed>();            
            _configPath = Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location);
            _smtp = new Smtp();
        }

        /// <summary>
        /// Returns the single instance of this object.
        /// </summary>
        public static Configuration Instance
        {
            get
            {
                // Lazy initialization.
                if (_instance == null)
                {
                    _instance = new Configuration();
                }

                return _instance;
            }            
        }

        [XmlIgnore]
        public string ConfigPath
        {
            get { return _configPath; }
            set { _configPath = value; }
        }
        
        public List<Subscriber> Subscribers
        {
            get { return _subscribers; }
            set { _subscribers = value; }
        }

        public List<Feed> Feeds
        {
            get { return _feeds; }
            set { _feeds = value; }
        }

        public Proxy Proxy
        {
            get { return _proxy; }
            set { _proxy = value; }
        }

        public Smtp Smtp
        {
            get { return _smtp; }
            set { _smtp = value; }
        }

        public static void Init()
        {
            Load();
            
            Instance.InitializeMonitoring();
        }

		private static void Load()
		{            
            LogManager.Info("Load configuration");

            StreamReader reader = null;
            XmlSerializer serializer = new XmlSerializer(typeof(Configuration));

            try
            {
                reader = new StreamReader(CONFIG_FILENAME);
                _instance = serializer.Deserialize(reader) as Configuration;
                
                LogManager.Info("Configuration loaded");
            }
            catch (IOException ex)
            {
                LogManager.Error("Error while loading configuration " + ex.ToString());
            }
            finally
            {
                if (reader != null)
                {
                    reader.Close();
                }
            }            
		}

        public void SaveUpdates()
        {
            // Temporarily disable watcher
            DisableWatcher();

            // Serialize configuration
            StreamWriter writer = new StreamWriter(CONFIG_FILENAME);
            XmlSerializer serializer = new XmlSerializer(typeof(Configuration));
            serializer.Serialize(writer, this);

            // Reenable watcher
            ReenableWatcher();
        }        

        private void watcher_Changed(object sender, FileSystemEventArgs e)
        {           
            // Load configuration
            Load();

            // Notify others about configuration changes            
            OnConfigurationUpdated(EventArgs.Empty);
        }

        private EventHandler _configurationUpdated = null;
                
        protected virtual void OnConfigurationUpdated(EventArgs e)
        {
            // Save the delegate field in a field for thread safety
            EventHandler handler = _configurationUpdated;

            // If any objects registered interest with event, notify them
            if (handler != null)
            {
                handler(this, e);
            }
        }

        private void InitializeMonitoring()
        {
            _watcher = new FileSystemWatcher();
            _watcher.Path = _configPath;
            _watcher.Filter = CONFIG_FILENAME;
            _watcher.EnableRaisingEvents = true;
            _watcher.IncludeSubdirectories = false;
            _watcher.NotifyFilter = NotifyFilters.LastWrite | NotifyFilters.Size;
            _watcher.Changed += new FileSystemEventHandler(watcher_Changed);
        }

        /// <summary>
        /// Temporarily disable watcher for monitoring events
        /// </summary>
        private void DisableWatcher()
        {
            _watcher.EnableRaisingEvents = false;
        }

        /// <summary>
        /// Enable watcher for monitoring events
        /// </summary>
        private void ReenableWatcher()
        {
            _watcher.EnableRaisingEvents = true;
        }

        public Feed GetFeed(Guid feedGuid)
        {
            foreach (Feed feed in _feeds)
            {
                if (feed.Guid.Equals(feedGuid))
                {
                    return feed;
                }
            }

            throw new ApplicationException(string.Format("Feed with Guid {0} was not found", feedGuid));
        }
    }
}
